Ext.ns('ru.proburo.oms.air.dao');

/**
 * ru.proburo.oms.air.dao.SyncManager
 * @extends Ext.util.Observable
 */
ru.proburo.oms.air.dao.SyncManager = Ext.extend(Ext.util.Observable, {
    
    count:      0,
    totalCount: 0,
    
    constructor: function(config)
    {
        config = config || {};
        
        var that = this;
        
        this.pBar = new Ext.ProgressBar({
            text:  '\u0418нициализация...',
            value: 0
        });
        
        this.title  = 'Синхронизация каталога';
        this.window = new Ext.Window({
            layout:      'fit',
            title:       this.title,
            width:       500,
            height:      48,
            closeAction: 'hide',
            closable:    false,
            resizable:   false,
            plain:       false,
            border:      false,
            modal:       true,
            
            items:       this.pBar
        });
        
        this.initEvents();
        
        this.on('start',    function(){this.window.show();}, this);
        this.on('complete', function(){this.window.hide();}, this);
        
        this.initTasks();
        
        ru.proburo.oms.air.dao.SyncManager.superclass.constructor.call(this, config);
    },
    
    initEvents: function()
    {
        this.addEvents({
			'start':        true,
            'progress':     true,
			'exception':    true,
            'taskfinished': true,
			'complete':     true
		});
    },
    
    initTasks: function()
    {
		var baseUrl = ru.proburo.oms.Constants.URL + '/services/update';
		
        this.tasks = [
            /*{
                name:      'элементы',
                url:       baseUrl + '/items',
                store:     new ru.proburo.oms.air.dao.WritableItemStore(),
                key:       'id',
                chunkSize: 100,
                remove:    false
            },
            {
                name:      'структура',
                url:       baseUrl + '/nodes',
                store:     new ru.proburo.oms.air.dao.WritableCatalogStore(),
                key:       'id',
                chunkSize: 25,
                remove:    false
            },
            {
                name:      'связи',
                url:       baseUrl + '/nodeitems',
                store:     new ru.proburo.oms.air.dao.WritableCatalogItemStore(),
                key:       ['node_id', 'item_id'],
                chunkSize: 300,
                remove:    false
            }*/
        ];
		
		this.syncOrders();
    },
    
    synchronize: function()
    {
        var that = this;
        
        this.fireEvent('start', this);
        
        var initTask = function(i)
        {
            if(that.tasks[i])
            {
                that.on('taskfinished', function(){
                    initTask(++i);
                }, that, {single: true});
                
                that._initTask(that.tasks[i]);
            }
            else
            {
                that.fireEvent('complete', that);
            }
        }        
        initTask(0);
    },
    
    _initTask: function(task)
    {
        this.window.setTitle(this.title + ': ' + task.name);
        
        task.store.on('load', function(store, records, options){
            this.doTask(task)
        }, this, {single: true});
        
        task.store.load();
    },
    
    
    
    
    
    
    
    doTask: function(task)
    {
        this.currentTask = task;
		
		Ext.Ajax.on('beforerequest', function(conn, options){
			console.info('Sending request...');
		}, this, {single: true});
		
        Ext.Ajax.request({
            url:     task.url,
            success: this.processResponse,
            failure: this.handleException,
            scope:   this,
            headers:
			{
				'User-Agent': [
					ru.proburo.oms.Constants.APPNAME,
					ru.proburo.oms.Constants.VERSION
				].join('/')
            }
         });
    },
    
    handleException: function(response, options)
    {
        console.debug('Wrong data recieved: '+response.status+', "'+escape(response.responseText.substr(0, 50))+'"');
		
		this.fireEvent('exception', this, response, options);
    },
    
    processResponse: function(response, options)
    {
		console.debug('Processing response: '+response.status+', "'+escape(response.responseText.substr(0, 50))+'"');
		
        var that      = this;
        var rs        = Ext.decode(response.responseText).items;        
        var len       = Number(rs.length);
        var store     = this.currentTask.store;
        var chunkSize = this.currentTask.chunkSize;
        
        this.pBar.updateProgress(0, '0%');
        
        var makeChunk = function(data)
        {
            if(data.length > 0)
            {            
                var i = 0;
                
                while(data.length > 0 && i < chunkSize)
                {
                    var row = data.pop();
                    
                    that.processData(store, row);
                    
                    var v = (len - data.length) / len;                    
                    that.pBar.updateProgress(v, Ext.util.Format.number(v*100, '0') + '%');
                    
                    i++;
                }
                
                if(!Ext.isEmpty(store.getModifiedRecords()))
                {
                    store.on('save', function(){
                        makeChunk(data);
                    }, this, {single: true});                    
                    store.save();
                }
                else
                {
                    makeChunk(data);
                }
            }
            else
            {
                that.fireEvent('taskfinished');
            }
        }
        makeChunk(rs);
    },
    
    processData: function(store, data)
    {
        var key = [];
        
        if(Ext.isPrimitive(this.currentTask.key))
        {
            key = data[this.currentTask.key];
        }
        else
        {
            for(var i in this.currentTask.key)
            {                
                if(false === this.currentTask.key.hasOwnProperty(i))
                {
                    continue;
                }
                
                var field = this.currentTask.key[i];
                
                key[field] = data[field];
            }
        }
        
        
        var record = this.getById(store, key);
        
        if(record)
        {
            record.beginEdit();
            
            for(var i in data)
            {
                if(false === data.hasOwnProperty(i)){ continue; };
                
                if(record.fields.containsKey(i))
                {
                    record.set(i, data[i]);
                }
            }
            
            record.endEdit();
        }
        else
        {
            record = new store.recordType();
            
            for(var i in data)
            {
                if(false === data.hasOwnProperty(i)){ continue; };
                
                if(record.fields.containsKey(i))
                {
                    record.set(i, data[i]);
                }
            }
            
            store.add(record);
        }
    },
    
    getById: function(store, key)
    {
        var record = null;
        
        if(Ext.isPrimitive(key))
        {
            record = store.getById(key);
        }
        else
        {
            var index = store.findBy(function(record){
                
                return record.get('node_id') == key['node_id'] && record.get('item_id') == key['item_id'];
                
                // FIX
                /*
                for(var i in key)
                {
                    if(false === key.hasOwnProperty(i)){ continue; }
                    
                    if(record.get(i) !== key[i])
                    {
                        return false;
                    }
                }
                
                return true;*/
            });
            
            if(-1 === index)
            {
                record = null;
            }
            else
            {
                record = store.getAt(index);
            }
        }
        
        return record;
    },
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	syncOrders: function()
	{
		this.remoteStore = new ru.proburo.oms.dao.JsonStore({
			url:    	ru.proburo.oms.Constants.URL + '/services/order/list.json',
            fields: 	ru.proburo.oms.order.entities.Order,
			baseParams: { status: -1 }
		});
		this.remoteStore.on('load', this._onRemoteStoreLoad, this);
		this.remoteStore.load();
	},
	
	_onRemoteStoreLoad: function()
	{
		this.localStore = new ru.proburo.oms.order.dao.OrderStore();		
		this.localStore.on('load', this._onLocalStoreLoad, this);		
		this.localStore.load();
	},
	
	_onLocalStoreLoad: function()
	{	
		this.localBatch  = {
			insert: [],
			update: []
		};
		this.remoteBatch = {
			insert: [],
			update: []
		};
		
		this.remoteStore.each(this._remoteStoreEach, this);
		this.localStore.each(this._localStoreEach, this);
		
		this._insert(this.localStore, this.localBatch['insert'], true);
		this._update(this.localStore, this.localBatch['update'], true, 'online_id', 'id');
		
		this.localStore.on('save', function(){
			
			this._insert(this.remoteStore, this.remoteBatch['insert'], false);
			this._update(this.remoteStore, this.remoteBatch['update'], false, 'id', 'online_id');
			
			this.remoteStore.save();
			
		}, this, {single: true});
		this.localStore.save();
		
		this._showDialog();
	},
	
	_remoteStoreEach: function(record)
	{
		var index = this.localStore.findExact('online_id', record.data['id']);
		
		if(-1 === index)
		{
			// record only in remote store, add to local
			this.localBatch['insert'].push(record);
		}
		else
		{
			var r = this.localStore.getAt(index);
			
			var diff = this._getModifiedDateDiff(record, r);
			
			if(diff > 0)
			{
				//(remote > local)
				this.localBatch['update'].push(record);
				console.log('"' + r.data.id + '" <= "' + record.data.id + '"');
			}
			else if(diff < 0)
			{
				//(remote < local)
				this.remoteBatch['update'].push(r);
				console.log('"' + r.data.id + '" => "' + record.data.id + '"');
			}
			else
			{
				// identical
				console.log('"' + record.data.id + '" = "' + r.data.id + '"');
			}
		}
	},
	
	_localStoreEach: function(record)
	{
		var index = this.remoteStore.findExact('id', record.data['online_id']);
		
		if(-1 === index)
		{
			// record only in local store, add to remote
			this.remoteBatch['insert'].push(record);
		}
	},
	
	
	
	_getModifiedDateDiff: function(r1, r2)
	{
		var field = 'modified_date';
		
		var v1 = r1.data[field] ? r1.data[field].valueOf() : 0 ;
		var v2 = r2.data[field] ? r2.data[field].valueOf() : 0 ;
		
		v1 = (v1 < 0) ? 0 : v1 ;
		v2 = (v2 < 0) ? 0 : v2 ;
		
		return v1 - v2;
	},
	
	_convert: function(record, local)
	{
		var data = Ext.apply({}, record.data);
		
		if(false === local)
		{
			delete data.online_id;
		}
		else
		{
			data.online_id = data.id;
		}
		
		delete data.id;
		delete data.sales_direction;
		delete data.customer;
		delete data.department;
		delete data.warehouse;		
		delete data.checkbox;
		
		return data;
	},
	
	_insert: function(store, batch, local)
	{		
		for(var i in batch)
		{
			if(false === batch.hasOwnProperty(i)){ continue; }
			
			var record = batch[i];
			
			var data = this._convert(record, local);
			
			var newRecord = new store.recordType(data);
			
			store.add(newRecord);
			
			// TODO properly set "online_id" to local rec
			if(false === local)
			{
				store.on('save', function(){
					record.set('online_id', newRecord.data.id);
					record.store.save();
				}, this, {single: true});
			}
		}
	},
	
	_update: function(store, batch, local, rkey, lkey)
	{
		for(var i in batch)
		{
			if(false === batch.hasOwnProperty(i)){ continue; }
			
			var rec   = store.getAt(store.find(rkey, batch[i][lkey]));
			
			var data  = this._convert(batch[i], local);
			
			rec.beginEdit();
			
			for(var i in data)
			{
				if(false === data.hasOwnProperty(i)){ continue; };
				
				if(rec.fields.containsKey(i))
				{
					rec.set(i, data[i]);
				}
			}
			
			rec.endEdit();
		}
	},
	
	
	
	
	
	
	
	
	
	_showDialog: function()
	{
		var w = new Ext.Window({
			layout:      'hbox',
			title:       'Orders',
			width:       800,
			height:      500,
			closeAction: 'hide',
			closable:    true,
			resizable:   false,
			plain:       false,
			border:      false,
			modal:       true,
			
			items:       [
				{
					xtype:    'grid',
					flex:  	  1,
					height:   500,
					store: 	  this.localStore,
					sm: 	  new Ext.grid.RowSelectionModel(),
					colModel: new Ext.grid.ColumnModel({
						columns: [
							{id: 'ID', 	  header: 'ID', 	  dataIndex: 'id'},
							{id: 'FK', 	  header: 'FK', 	  dataIndex: 'online_id'},
							{id: 'modified_date', header: 'Modified', dataIndex: 'modified_date', renderer: this._dateRenderer},
							new ru.proburo.oms.air.components.grid.RadioColumn({
								dataIndex: 	 'checkbox',
								remoteStore: this.remoteStore
							})
						]
					}),
					viewConfig:
					{
						forceFit: true
					}
				},
				{
					xtype:	  'grid',
					flex:  	  1,
					height:   500,
					store: 	  this.remoteStore,
					sm: 	  new Ext.grid.RowSelectionModel(),
					colModel: new Ext.grid.ColumnModel({
						columns: [
							{id: 'ID', 	  header: 'ID', 	  dataIndex: 'id'},
							{id: 'modified_date', header: 'Modified', dataIndex: 'modified_date', renderer: this._dateRenderer},
							new ru.proburo.oms.air.components.grid.RadioColumn({
								dataIndex: 	 'checkbox',
								remoteStore: this.localStore
							})
						]
					}),
					viewConfig:
					{
						forceFit: true
					}
				}
			],
			bbar: [{text:'OK'}]
		});
		w.show();
	},
	
	_dateRenderer: function(v, params, data)
	{
		return (v && v.dateFormat && v.valueOf() > 0) ? v.dateFormat("m/d/Y H:i") : '';
	},
    
    
    
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
    
    
    
    
    
    
    
    syncItemImages: function()
    {
        var wStore = new ru.proburo.oms.air.dao.WritableItemStore();
        
        wStore.on('load', function(store, records, options){
            Ext.each(records, function(record){
                //this.doRequest('http://images.proburo.ru');
            });
            
            this._onSave()
        }, this, {single: true});
        
        wStore.load();
    }/*,
    
    
    
    
    
    
    
    
    
    
    
    
    doRequest: function(url)
    {
        this.baseUrl = ru.proburo.oms.Constants.URL + '/services';
        this.loader  = new air.URLLoader();
        this.loader.dataFormat = air.URLLoaderDataFormat.BINARY;
        this.loader.addEventListener(air.Event.COMPLETE,                        function(e){that._onComplete(e);});
        this.loader.addEventListener(air.IOErrorEvent.IO_ERROR,                 function(e){that._onError(e);});
        this.loader.addEventListener(air.HTTPStatusEvent.HTTP_RESPONSE_STATUS,  function(e){that._onStatus(e);});
        this.loader.addEventListener(air.ProgressEvent.PROGRESS,                function(e){that._onProgress(e);});
        
        var request = new air.URLRequest(this.baseUrl + url);
        var params = new air.URLVariables();
        params.node  = 9324;
        params.limit = 1;
        
        request.data   = params;
        request.method = air.URLRequestMethod.POST;
        
        try
        {
            this.loader.load(request);
        }
        catch(error)
        {
            console.log("Unable to load requested document.");
        }
    },
    
    _onComplete: function(e)
    {
        var data = this.loader.data;
        console.log('SYNC Data: ' + data);
    },
    
    _onError: function(e)
    {
        console.log('SYNC Error: ' + e.text);
    },
    
    _onStatus: function(e)
    {
        console.log('SYNC HTTP-Status: ' + e.status);
    },
    
    _onProgress: function(e)
    {
        var data = this.loader.data;
        console.log('SYNC Progress: ' + e.bytesLoaded + ' of ' + e.bytesTotal + ' bytes loaded.');
    }*/
});